Crate mirror_mirror

source ·
Expand description

General purpose reflection library for Rust.

§Examples

§Access a field by its string name and mutate it

use mirror_mirror::{Reflect, Struct};

#[derive(Reflect, Clone, Debug)]
struct Foo {
    x: i32,
}

let mut foo = Foo { x: 42 };

// Get a `Struct` trait object for `Foo`.
//
// The `Struct` trait has methods available for all structs such as accessing
// fields by name and iterating over the fields.
let struct_obj: &mut dyn Struct = foo.as_struct_mut()?;

// Mutably borrow the `x` field. We can access fields using string names.
let x: &mut dyn Reflect = struct_obj.field_mut("x")?;

// Downcast `x` into a mutable `i32`
let x: &mut i32 = x.downcast_mut::<i32>()?;

// Change the value of `x`
*x += 1;

// The value of `x` in `foo` has now changed.
assert_eq!(foo.x, 43);

§Iterate over all fields

use mirror_mirror::{Reflect, Struct, ReflectMut, ScalarMut, enum_::VariantFieldMut};

// A function that iterates over the fields in an enum and mutates them.
fn change_enum_fields(value: &mut dyn Reflect) -> Option<()> {
    let enum_ = value.as_enum_mut()?;

    for field in enum_.fields_mut() {
        match field {
            VariantFieldMut::Struct(_, value) | VariantFieldMut::Tuple(value) => {
                match value.reflect_mut() {
                    ReflectMut::Scalar(ScalarMut::i32(n)) => {
                        *n *= 2;
                    }
                    ReflectMut::Scalar(ScalarMut::String(s)) => {
                        *s = format!("{s}bar");
                    }
                    // Ignore other types
                    _ =>  {}
                }
            }
        }
    }

    Some(())
}

#[derive(Reflect, Clone, Debug)]
enum Bar {
    X { x: i32 },
    Y(String),
}

let mut bar = Bar::X { x: 42 };
change_enum_fields(bar.as_reflect_mut())?;

assert!(matches!(bar, Bar::X { x: 84 }));

let mut bar = Bar::Y("foo".to_owned());
change_enum_fields(bar.as_reflect_mut())?;

assert!(matches!(bar, Bar::Y(s) if s == "foobar"));

§Query value and type information using key paths

use mirror_mirror::{
    Reflect,
    key_path,
    key_path::{GetPath, GetTypePath, field},
    type_info::{DescribeType, ScalarType},
};

// Some complex nested data type.
#[derive(Reflect, Clone, Debug)]
struct User {
    employer: Option<Company>,
}

#[derive(Reflect, Clone, Debug)]
struct Company {
    countries: Vec<Country>,
}

#[derive(Reflect, Clone, Debug)]
struct Country {
    name: String
}

let user = User {
    employer: Some(Company {
        countries: vec![Country {
            name: "Denmark".to_owned(),
        }],
    }),
};

// Build a key path that represents accessing `.employer::Some.0.countries[0].name`.
//
// `::Some` means to access the `Some` variant of `Option<Company>`.
let path = field("employer").variant("Some").field(0).field("countries").get(0).field("name");

// Get the value at the key path.
assert_eq!(user.get_at::<String>(&path).unwrap(), "Denmark");

// Key paths can also be constructed using the `key_path!` macro.
// This invocation expands the same code we have above.
let path = key_path!(.employer::Some.0.countries[0].name);

// Use the same key path to query type information. You don't need a value
// of the type to access its type information.
let user_type = <User as DescribeType>::type_descriptor();
assert!(matches!(
    user_type.type_at(&path).unwrap().as_scalar().unwrap(),
    ScalarType::String,
));

§Using opaque Value types

use mirror_mirror::{Reflect, Value, FromReflect};

#[derive(Reflect, Clone, Debug)]
struct Foo(Vec<i32>);

let foo = Foo(vec![1, 2, 3]);

// Convert `foo` into general "value" type.
let mut value: Value = foo.to_value();

// `Value` also implements `Reflect` so it can be mutated in the
// same way we've seen before. So these mutations can be made
// by another crate that doesn't know about the `Foo` type.
//
// `Value` is also serializable with `speedy`, for binary serialization,
// or `serde`, for everything else.
value
    .as_tuple_struct_mut()?
    .field_at_mut(0)?
    .as_list_mut()?
    .push(&4);

// Convert the `value` back into a `Foo`.
let new_foo = Foo::from_reflect(&value)?;

// Our changes were applied.
assert_eq!(new_foo.0, vec![1, 2, 3, 4]);

§Inspiration

The design of this library is heavily inspired by bevy_reflect but with a few key differences:

  • speedy integration which is useful for marshalling data perhaps to send it across FFI.
  • A Value type that can be serialized and deserialized without using trait objects.
  • More type information captured.
  • Add meta data to types which becomes part of the type information.
  • Key paths for querying value and type information.
  • No dependencies on bevy specific crates.
  • #![no_std] support.

§Feature flags

mirror-mirror uses a set of [feature flags] to optionally reduce the number of dependencies.

The following optional features are available:

NameDescriptionDefault?
stdEnables using the standard library (core and alloc are always required)Yes
speedyEnables speedy support for most typesYes
serdeEnables serde support for most typesYes
glamEnables impls for glamNo
macawEnables impls for macawNo

Modules§

Macros§

Structs§

Enums§

Traits§

  • A reflected array type.
  • Trait for accessing type information.
  • A reflected enum type.
  • A trait for types which can be constructed from a reflected type.
  • Helper trait for accessing and downcasting fields on reflected values.
  • Helper trait for mutably accessing and downcasting fields on reflected values.
  • A reflected list type.
  • A reflected map type.
  • A reflected type.
  • A reflected struct type.
  • A reflected tuple type.
  • A reflected tuple struct type.

Functions§

Derive Macros§

  • Derive an implementation of Reflect and other appropriate traits.